#region References

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using SpoolFileLibrary.Reader.Misc;
using SpoolFileLibrary.Reader.Winspool;

#endregion

namespace SpoolFileLibrary.Reader.Shadow {
    internal class ShadowFile : IShadowFile {
        private readonly BinaryReader binaryReader;
        private readonly IList<IPage> pages;
        private DeviceMode deviceMode;

        // Fields
        private string computername;
        private string dataFormat;
        private DEVMODE devMode;
        private string documentName;
        private string driverName;
        private int jobID;
        private string notifyName;
        private int pageCount;
        private string port;
        private string printerName;
        private string printProcessor;
        private int priority;
        private int size;
        private int size2;
        private DateTime startTime;
        private DateTime submitted;
        private DateTime untilTime;
        private string userName;

        public ShadowFile(Stream stream) {
            if(stream == null) {
                throw new ArgumentNullException("stream");
            }
            stream.Seek(0, SeekOrigin.Begin);
            this.binaryReader = new BinaryReader(stream, Encoding.Unicode);
            this.pages = new List<IPage>();
        }

        #region ISpoolFile Members

        public bool Parse(bool parsePages, int scaleFactor) {
            HeaderRecord headerRecord = this.ParseHeaderRecord();

            this.devMode = ParsingHelper.ExtractDevMode(binaryReader, headerRecord.offDevMode);

            this.jobID = headerRecord.dwJobID;
            this.priority = headerRecord.dwPriority;
            this.userName = ParsingHelper.ReadString(binaryReader, headerRecord.offUserName);
            this.notifyName = ParsingHelper.ReadString(binaryReader, headerRecord.offNotifyName);
            this.documentName = ParsingHelper.ReadString(binaryReader, headerRecord.offDocumentName);
            this.port = ParsingHelper.ReadString(binaryReader, headerRecord.offPort);
            this.printerName = ParsingHelper.ReadString(binaryReader, headerRecord.offPrinterName);
            this.driverName = ParsingHelper.ReadString(binaryReader, headerRecord.offDriverName);
            this.printProcessor = ParsingHelper.ReadString(binaryReader, headerRecord.offPrintProcessor);
            this.dataFormat = ParsingHelper.ReadString(binaryReader, headerRecord.offDataFormat);
            this.submitted = new DateTime(headerRecord.stSubmitTime.wYear, headerRecord.stSubmitTime.wMonth, headerRecord.stSubmitTime.wDay, headerRecord.stSubmitTime.wHour, headerRecord.stSubmitTime.wMinute, headerRecord.stSubmitTime.wSecond, headerRecord.stSubmitTime.wMilliseconds);
            this.startTime = (headerRecord.dwStartTime > 0) ? new DateTime(headerRecord.dwStartTime) : DateTime.MinValue;
            this.untilTime = (headerRecord.dwUntilTime > 0) ? new DateTime(headerRecord.dwUntilTime) : DateTime.MaxValue;
            this.size = headerRecord.dwSizeSPL;
            this.pageCount = headerRecord.dwPageCount;
            this.computername = ParsingHelper.ReadString(binaryReader, headerRecord.offComputername);
            this.size2 = headerRecord.dwSPLSize2;

            this.deviceMode = new DeviceMode(this.devMode);

            return true;
        }

        public string DocumentName {
            get {
                return this.documentName;
            }
        }

        public string Computername {
            get {
                return this.computername;
            }
        }

        public string NotifyName {
            get {
                return this.notifyName;
            }
        }

        public string PrinterName {
            get {
                return this.printerName;
            }
        }

        public string UserName {
            get {
                return this.userName;
            }
        }

        public DateTime Submitted {
            get {
                return this.submitted;
            }
        }
 
        public SpoolFileFormat SpoolFileFormat {
            get { return SpoolFileFormat.Shadow; }
        }

        public PrintjobLanguage PrintjobLanguage {
            get { return PrintjobLanguage.UNKNOWN; }
        }

        public bool Parse() {
            return this.Parse(false, 0);
        }

        public IList<IPage> Pages {
            get { return this.pages; }
        }

        public int Copies {
            get { return this.deviceMode.Copies; }
        }

        public bool Color {
            get { return this.deviceMode.Color; }
        }

        public int PaperLength {
            get { return this.deviceMode.PaperLength; }
        }

        public int PaperWidth {
            get { return this.deviceMode.PaperWidth; }
        }

        public bool Landscape {
            get { return this.deviceMode.Orientation == Orientation.Landscape; }
        }

        public IDeviceMode DeviceMode {
            get { return this.deviceMode; }
        }

        public byte[] RawData {
            get { return null; }
        }

        #endregion

        public DateTime UntilTime {
            get { return untilTime; }
        }

        public DateTime StartTime {
            get { return startTime; }
        }

        public int Size2 {
            get { return size2; }
        }

        public int Size {
            get { return size; }
        }

        public int Priority {
            get { return priority; }
        }

        public string PrintProcessor {
            get { return printProcessor; }
        }

        public string Port {
            get { return port; }
        }

        public int PageCount {
            get { return pageCount; }
        }

        public int JobId {
            get { return jobID; }
        }

        public string DriverName {
            get { return driverName; }
        }

        public string DataFormat {
            get { return dataFormat; }
        }

        private HeaderRecord ParseHeaderRecord() {
            var headerRecord = new HeaderRecord {stSubmitTime = new SYSTEMTIME()};

            binaryReader.BaseStream.Position = 0L;
            headerRecord.dwSignature = binaryReader.ReadInt32();
            var dwSignature = (ShadowSignature)headerRecord.dwSignature;
            if (dwSignature >= ShadowSignature.SHD_SIGNATURE_WIN2000) {
                headerRecord.dwHeaderSize = binaryReader.ReadInt32();
            }
            headerRecord.wStatus = binaryReader.ReadInt16();
            headerRecord.wUnknown1 = binaryReader.ReadInt16();
            headerRecord.dwJobID = binaryReader.ReadInt32();
            headerRecord.dwPriority = binaryReader.ReadInt32();
            headerRecord.offUserName = binaryReader.ReadInt32();
            headerRecord.offNotifyName = binaryReader.ReadInt32();
            headerRecord.offDocumentName = binaryReader.ReadInt32();
            headerRecord.offPort = binaryReader.ReadInt32();
            headerRecord.offPrinterName = binaryReader.ReadInt32();
            headerRecord.offDriverName = binaryReader.ReadInt32();
            headerRecord.offDevMode = binaryReader.ReadInt32();
            headerRecord.offPrintProcessor = binaryReader.ReadInt32();
            headerRecord.offDataFormat = binaryReader.ReadInt32();
            headerRecord.dwUnknown2 = binaryReader.ReadInt32();
            headerRecord.stSubmitTime.wYear = binaryReader.ReadUInt16();
            headerRecord.stSubmitTime.wMonth = binaryReader.ReadUInt16();
            headerRecord.stSubmitTime.wDayOfWeek = binaryReader.ReadUInt16();
            headerRecord.stSubmitTime.wDay = binaryReader.ReadUInt16();
            headerRecord.stSubmitTime.wHour = binaryReader.ReadUInt16();
            headerRecord.stSubmitTime.wMinute = binaryReader.ReadUInt16();
            headerRecord.stSubmitTime.wSecond = binaryReader.ReadUInt16();
            headerRecord.stSubmitTime.wMilliseconds = binaryReader.ReadUInt16();
            headerRecord.dwStartTime = binaryReader.ReadInt32();
            headerRecord.dwUntilTime = binaryReader.ReadInt32();
            headerRecord.dwSizeSPL = binaryReader.ReadInt32();
            headerRecord.dwPageCount = binaryReader.ReadInt32();
            headerRecord.dwSizeSecurityInfo = binaryReader.ReadInt32();
            headerRecord.offSecurityInfo = binaryReader.ReadInt32();
            headerRecord.dwUnknown3 = binaryReader.ReadInt32();
            if (dwSignature >= ShadowSignature.SHD_SIGNATURE_WINNT) {
                headerRecord.dwUnknown4 = binaryReader.ReadInt32();
                headerRecord.dwUnknown5 = binaryReader.ReadInt32();
            }
            if (dwSignature >= ShadowSignature.SHD_SIGNATURE_WIN2000) {
                headerRecord.offComputername = binaryReader.ReadInt32();
                headerRecord.dwSPLSize2 = binaryReader.ReadInt32();
            }


            return headerRecord;
        }
    }
}